Spring AOP中的Pointcut
本文相关代码(来自官方源码spring-test模块)请参见spring-framework org.springframework.mylearntest包下。
如果Pointcut类型为TruePointcut,默认会对系统中的所有对象,以及对象上所有被支持的Joinpoint进行匹配。
Pointcut
package org.springframework.aop;
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
TruePointcut
package org.springframework.aop;
import java.io.Serializable;
@SuppressWarnings("serial")
final class TruePointcut implements Pointcut, Serializable {
public static final TruePointcut INSTANCE = new TruePointcut();
private TruePointcut() {
}
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "Pointcut.TRUE";
}
}
ClassFilter和MethodMatcher分别用于匹配将被执行织入操作的对象以及相应的方法。之所以将类型匹配和方法匹配分开定义,是因为可以重用不同级别的匹配定义,并且可以在不同级别或者相同级别上进行组合操作,或者强制让某个子类只覆盖(Override)相应方法定义等。
ClassFilter
package org.springframework.aop;
@FunctionalInterface
public interface ClassFilter {
boolean matches(Class<?> clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
MethodMatcher
package org.springframework.aop;
import java.lang.reflect.Method;
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
当isRuntime返回false时,表示不会考虑具体Joinpoint的方法参数,这种类型的MethodMatcher称之为staticMethodMatcher。因为不用每次都检查参数,那么对于同样类型的方法匹配结果,就可以在框架内部缓存以提高性能。
当isRuntime返回true时,表明MethodMatcher将会每次都对方法调用的参数进行匹配检查,这种类型的MethodMatcher称之为DynamicMethodMatcher。因为每次都要对方法参数进行检查,无法对匹配的结果进行缓存,所以,匹配效率相对于StaticMethodMatcher来说要差。而且大部门情况下,staticMethodMatcher已经可以满足需要。最好避免使用DynamicMethodMatcher类型。
如果boolean matches(Method method, Class<?> targetClass);
返回true时,三个参数的matches将会被执行,以进一步检查匹配条件;如果boolean matches(Method method, Class<?> targetClass);
返回false,那么不管这个MethodMatcher是staticMethodMatcher还是DynamicMethodMatcher,该结果已经是最终结果,三个参数的方法肯定不会被执行了。
NameMatchMethodPointcut
最简单的Pointcut实现,属于StaticMethodMatcherPointcut的子类,可以根据自身指定一组方法名称与Joinpoint处的方法的方法名称进行匹配。
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedName("matches");
// 或者传入多个方法名
pointcut.setMappedNames(new String[]{"matches", "isRuntime"});
// 简单模糊匹配
pointcut.setMappedNames(new String[]{"match*", "matches", "mat*es" });
此方法无法对重载的方法名进行匹配,因为它仅对方法名进行匹配,不会考虑参数相关信息,而且也没有提供可以指定参数匹配信息的途径。
JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut
StaticMethodMatcherPointcut的子类有一个专门提供基于正则表达式的实现分支,以抽象类AbstractRegexpMethodPointcut为统帅,声明了pattern 和 patterns属性,可以指定一个或者和多个正则表达式的匹配模式。其下设JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut两种具体实现。
JdkRegexpMethodPointcut是在JDK 1.4之后引入JDK标准正则表达式。
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*match.*");
pointcut.setPatterns(new String[]{".*match.", ".*matches"});
注意正则表达式匹配模式必须匹配整个方法签名(Method signature)的形式指定,而不能像NameMatchMethodPointcut那样仅给出匹配的方法名称。
Perl5RegexpMethodPointcut实现使用Jakarta ORO提供正则表达式支持
- 可以通过pattern或者patterns对象属性指定一个或者多个正则表达式
- 指定正则表达式匹配模式应该覆盖匹配整个方法签名,而不是只指定到方法名称部分。
AnnotationMatchingPointcut
ClassLevelAnnotation
package org.springframework.mylearntest.aop.annotationmatchingpointcut;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassLevelAnnotation {
}
MethodLevelAnnotation
package org.springframework.mylearntest.aop.annotationmatchingpointcut;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodLevelAnnotation {
}
GenericTargetObject
package org.springframework.mylearntest.aop.annotationmatchingpointcut;
@ClassLevelAnnotation
public class GenericTargetObject {
@MethodLevelAnnotation
public void getMethod1() {
System.out.println("getMethod1");
}
public void getMethod2() {
System.out.println("getMethod2");
}
}
如何指定Pointcut
AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class);
// 也可以通过静态方法
AnnotationMatchingPointcut pointcut1 = AnnotationMatchingPointcut.forClassAnnotation(MethodLevelAnnotation.class);
// 同时限定
AnnotationMatchingPointcut pointcut2 = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class);
ComposablePointcut
Spring AOP提供Pointcut逻辑运算的Pointcut实现。它可以进行Pointcut之间的“并”以及“交”运算。
Test4ComposablePointcut
package org.springframework.mylearntest.aop.pointcut.composablePointcut;
import org.junit.Assert;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.Pointcuts;
public class Test4ComposablePointcut {
public static void main(String[] args) {
ComposablePointcut pointcut1 = new ComposablePointcut(new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return false;
}
}, MethodMatcher.TRUE);
ComposablePointcut pointcut2 = new ComposablePointcut(new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return false;
}
}, MethodMatcher.TRUE);
// union intersection
ComposablePointcut union = pointcut1.union(pointcut2);
ComposablePointcut intersection = pointcut1.intersection(union);
Assert.assertEquals(pointcut1,intersection);
// combine classFilter with methodMatcher
pointcut2.union(new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return false;
}
}).intersection(MethodMatcher.TRUE);
// just compute between pointcut, use org.springframework.aop.support.Pointcuts
Pointcut pointcut3 = new Pointcut() {
@Override
public ClassFilter getClassFilter() {
return null;
}
@Override
public MethodMatcher getMethodMatcher() {
return null;
}
};
Pointcut pointcut4 = new Pointcut() {
@Override
public ClassFilter getClassFilter() {
return null;
}
@Override
public MethodMatcher getMethodMatcher() {
return null;
}
};
Pointcut union1 = Pointcuts.union(pointcut3, pointcut4);
Pointcut intersection1 = Pointcuts.intersection(pointcut3, pointcut4);
}
}
ControlFlowPointcut
ControlFlowPointcut匹配程序 的调用流程,不是对某个方法执行所在Joinpoint处的单一特征进行匹配,而是要被特定的类执行时,才会进行方法拦截。 因为ControlFlowPointcut类型的Pointcut需要在运行期间检查程序的调用栈,而且每次方法调用都需要检查,所以性能比较差。